{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Лекция 4. Классы продолжение"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Спецификация вызова конструктора базового класса"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Базовый класс может иметь несколько конструкторов, а наследник может выбирать, какой именно из конструкторов он хочет вызвать. Это определяется в списке инициализации конструктора"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "public:\n",
    "    Animal();\n",
    "    Animal(const std::string& a_name);\n",
    "    Animal(const std::string& a_name, unsigned a_age);\n",
    "\n",
    "private:\n",
    "    std::string name;\n",
    "    unsigned age;\n",
    "};\n",
    "\n",
    "class Turtle : public Animal\n",
    "{\n",
    "public:\n",
    "    // неявно вызывает Animal()\n",
    "    Turtle() {}    \n",
    "    // эквивалентно записи:\n",
    "    // Turtle() : Animal() {}\n",
    "    \n",
    "    // явный вызов конкретного конструктора Animal\n",
    "    Turtle(const std::string& name)\n",
    "        : Animal(name)  // сначала вызываем конструктор базового класса\n",
    "        , color(White)  // затем конструкторы членов в порядке их определения\n",
    "    {\n",
    "    }\n",
    "    \n",
    "private:\n",
    "    Color color = Black;  // значение для инициализации по умолчанию\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Виртуальные функции"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Задача, которую решает мезанизм виртуальных функций: класс-наследник может __уточнять__ поведение базового класса"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Примеры:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "public:\n",
    "    virtual void cry() {}\n",
    "};\n",
    "\n",
    "class Dog : public Animal\n",
    "{\n",
    "public:\n",
    "    void cry() {\n",
    "        std::cout << \"augh!\" << std::endl;\n",
    "    }\n",
    "};\n",
    "\n",
    "class Cat : public Animal\n",
    "{\n",
    "public:\n",
    "    void cry() {\n",
    "        std::cout << \"meow!\" << std::endl;\n",
    "    }\n",
    "};\n",
    "```\n",
    "\n",
    "__Вопрос знатокам__:\n",
    "\n",
    "<details>\n",
    "<summary>Какая ошибка здесь допущена?</summary>\n",
    "<p>\n",
    "    \n",
    "Нет виртуального деструктора у `Animal`. Объяснение на семинаре.\n",
    "    \n",
    "</p>    \n",
    "</details>\n",
    "\n",
    "<details>\n",
    "<summary>Почему это не страшно? (но поправить ошибку всё равно желательно)</summary>\n",
    "<p>\n",
    "    \n",
    "Деструкторы Cat && Dog ничего не делает.\n",
    "    \n",
    "</p>    \n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "void make_it_cry(Animal& animal)\n",
    "{\n",
    "    animal.cry();\n",
    "    animal.cry();\n",
    "    animal.cry();\n",
    "}\n",
    "\n",
    "Animal animal;\n",
    "make_it_cry(animal);\n",
    "\n",
    "Dog dog;\n",
    "make_it_cry(dog);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "А что здесь?\n",
    "\n",
    "```c++\n",
    "void make_it_cry_more(Animal animal)\n",
    "{\n",
    "    for (int i = 0; i < 100; ++i)\n",
    "        animal.cry();\n",
    "}\n",
    "\n",
    "Animal animal;\n",
    "make_it_cry_more(animal);\n",
    "\n",
    "Dog dog;\n",
    "make_it_cry_more(dog);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Чисто виртуальные функции"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Метод `cry()` не имеет смысла для класса `Animal`, т.к. не понятно, какая реализация у него должна быть. Можно указать, что у виртуальной функции `Animal::cry()` нет реализации. Такие функции называются **чисто виртуальными**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "public:\n",
    "    virtual void cry() = 0;\n",
    "};\n",
    "\n",
    "// далее аналогично ...\n",
    "class Dog : public Animal { ... };\n",
    "class Cat : public Animal { ... };\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Вопрос:__\n",
    "\n",
    "<details>\n",
    "<summary>Что ещё принципиально поменялось для класса Animal?</summary>\n",
    "<p>\n",
    "\n",
    "Объекты типа `Animal` нельзя создавать.\n",
    "\n",
    "</p>\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ещё пример:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class BridgeBuilder\n",
    "{\n",
    "public:\n",
    "    virtual Bridge make() = 0;    \n",
    "};\n",
    "\n",
    "class StoneBridgeBuilder : public BridgeBuilder\n",
    "{\n",
    "public:\n",
    "    Bridge make() { return Bridge(\"stone\"); }    \n",
    "};\n",
    "\n",
    "class WoodBridgeBuilder : public BridgeBuilder\n",
    "{\n",
    "public:\n",
    "    Bridge make() { return Bridge(\"wood\"); }    \n",
    "};\n",
    "\n",
    "Landscape make_landscape(BridgeBuilder& bridge_builder)\n",
    "{\n",
    "    const auto bridge1 = bridge_builder.make();\n",
    "    const auto bridge2 = bridge_builder.make();\n",
    "    ...;\n",
    "}\n",
    "```\n",
    "\n",
    "__Вопрос знатокам:__\n",
    "\n",
    "<details>\n",
    "<summary>Что-нибудь напоминает?</summary>\n",
    "<p>\n",
    "\n",
    "Abstract Factory pattern.\n",
    "\n",
    "</p>\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "С философской точки зрения ООП чисто виртуальные функции лучше отражают ООП-модель программы. Но и обычные виртуальные функции применимы.\n",
    "\n",
    "__Вопрос:__ можете ли привести пример?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Пример:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "// класс для работы с сетью\n",
    "class Network\n",
    "{\n",
    "public:\n",
    "    virtual HttpResponse get(const std::string& url) { /*...*/ }\n",
    "};\n",
    "\n",
    "// класс для тестирования медленной сети\n",
    "class NetworkSlow : public Network\n",
    "{\n",
    "public:\n",
    "    virtual HttpResponse get(const std::string& url) {\n",
    "        // спим\n",
    "        std::chrono::sleep_for(100_ms);\n",
    "        \n",
    "        // вызов базового класса\n",
    "        return Network::get(url);\n",
    "    }\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### virtual / override / final"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* `virtual` - сделать метод виртуальным, чтобы наследники могли его переопределять\n",
    "* `override` - указание компилятору убедиться, что метод виртуальный, если нет - ошибка компиляции\n",
    "* `final` - запретить наследникам переопределять виртуальный метод"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Зачем нужен `override`:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Иерархия сегодня:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "public:\n",
    "    virtual void cry() {}\n",
    "};\n",
    "\n",
    "class Dog : public Animal\n",
    "{\n",
    "public:\n",
    "    void cry() { std::cout << \"augh!\"; }\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Иерархия через пару месяцев:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "public:\n",
    "    virtual void cry(bool loud) {}\n",
    "};\n",
    "\n",
    "class Dog : public Animal\n",
    "{\n",
    "public:\n",
    "    void cry() { std::cout << \"augh!\"; } // OOOPS, it comiles but is not working as expected anymore\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Как надо было делать иерархию \"сегодня\":"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "public:\n",
    "    virtual void cry(bool cloud) {}    \n",
    "};\n",
    "\n",
    "class Dog : public Animal\n",
    "{\n",
    "public:\n",
    "    void cry() override { std::cout << \"augh!\"; }\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Тогда ошибку поймал бы компилятор, а не пользователь."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### vftable"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Один из способов реализации механизмов виртуальных функций - компилятор может вписать в класс дополнительное поле - указатель на таблицу виртуальных функций:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "    std::uint64_t age;\n",
    "public:\n",
    "    virtual void cry();\n",
    "    virtual void jump();\n",
    "};\n",
    "\n",
    "class Dog : public Animal\n",
    "{\n",
    "    std::uint64_t color;\n",
    "public:\n",
    "    void cry() override;\n",
    "    void jump() override;\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](vftable.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Вопросы:\n",
    "\n",
    "<details>\n",
    "<summary>sizeof(Animal)</summary>\n",
    "16\n",
    "</details>\n",
    "    \n",
    "<details>\n",
    "<summary>sizeof(Dog)</summary>\n",
    "24\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Множественное наследование, ромбовидное наследование"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Обратить внимание на порядок конструирования, вызова деструкторов и способа разрешения конфликтов по именам"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal {};\n",
    "\n",
    "class JumpingCreature\n",
    "{\n",
    "public:\n",
    "    void jump();\n",
    "};\n",
    "\n",
    "class CryingCreature\n",
    "{\n",
    "public:\n",
    "    void cry();    \n",
    "};\n",
    "\n",
    "\n",
    "class Cat : public Animal\n",
    "          , public JumpingCreature\n",
    "          , public CryingCreature\n",
    "{\n",
    "public:\n",
    "    void play()\n",
    "    {\n",
    "        jump();\n",
    "        cry();\n",
    "        jump();\n",
    "        \n",
    "        // явное указание метода,\n",
    "        //\n",
    "        // потребуется, если метод jump()\n",
    "        // есть и у JumpingCreature, и у Animal\n",
    "        JumpingCreature::jump();\n",
    "        CryingCreature::cry();\n",
    "    }\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"classes_diagram_cat.png\" height=500px width=500px />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Граф наследования ацикличен и однонаправлен... но ничто не мешает сделать в нём ромб. Такая конструкция выглядит необычной, но, тем не менее, используется даже в стандартной библиотеке:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](std-basic_iostream-inheritance.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.cppreference.com/w/cpp/io/basic_iostream"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Layout"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Рассмотрим как устроено расположение класса в памяти. Пойдём от простого к сложному."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "struct Point\n",
    "{\n",
    "    float x;  // size = 4, alignment = 4\n",
    "    float y;  // size = 4, alignment = 4\n",
    "};  // size = 8, alignment = 4\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](layout_point.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class A\n",
    "{\n",
    "    float x;          // size = 4, alignment = 4\n",
    "    std::uint64_t y;  // size = 8, alignment = 8  \n",
    "};  // size = ???, alignment = ???\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](layout_padding.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Более подробно про alignment:\n",
    "https://en.cppreference.com/w/c/language/object"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class A\n",
    "{\n",
    "    float x;          // size = 4, alignment = 4\n",
    "    std::uint64_t y;  // size = 8, alignment = 8\n",
    "    float z;          // size = 4, alignment = 4\n",
    "};  // size = ???, alignment = ???\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](layout_padding_2.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class A\n",
    "{\n",
    "    std::uint64_t y;  // size = 8, alignment = 8\n",
    "    float x;          // size = 4, alignment = 4\n",
    "    float z;          // size = 4, alignment = 4\n",
    "};  // size = ???, alignment = ???\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](layout_padding_3.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Рассмотрим layout класса в памяти в случае наследования:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "struct P\n",
    "{\n",
    "    float x;  // size = 4, alignment = 4\n",
    "    float y;  // size = 4, alignment = 4\n",
    "};  // size = 8, alignment = 4\n",
    "\n",
    "struct WP : P\n",
    "{\n",
    "    float w;  // size = 4, alignment = 4\n",
    "};  // size = ???, alignment = ???\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](layout_inheritance.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Рассмотрим layout класса в памяти в случае множественного наследования:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class CBase1 { ... };\n",
    "class CBase2 { ... };\n",
    "\n",
    "class CDerived : public CBase1\n",
    "               , public CBase2\n",
    "{ ... };\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](layout_multiple_inheritance.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Как быть с виртуальными методами и функциями:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Base\n",
    "{\n",
    "public:\n",
    "    virtual void say_hello();\n",
    "    virtual void say_goodbye();\n",
    "    \n",
    "private:\n",
    "    ...\n",
    "};\n",
    "\n",
    "class Dervied : public Base\n",
    "{\n",
    "public:\n",
    "    void say_hello() override;\n",
    "    void say_goodbye() override;\n",
    "    \n",
    "private:\n",
    "    ...\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](layout_vtable_3.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Показать как ведёт себя преобразование указателей для такого лаяута (и почему с reinterpert_cast лучше не переборщить).\n",
    "\n",
    "Открыть на godbolt.org пример и показать как происходит сравнение указателей, закомментировать виртуальные функции - посмотреть что происходит."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "struct Base1\n",
    "{\n",
    "    // virtual void say_hello() = 0;\n",
    "    float x;\n",
    "};\n",
    "\n",
    "struct Base2\n",
    "{\n",
    "    float x;\n",
    "};\n",
    "\n",
    "struct Derived : public Base1\n",
    "               , public Base2\n",
    "{\n",
    "    // virtual void say_hello() = 0;\n",
    "    float y;\n",
    "};\n",
    "\n",
    "bool equal1(Base1* lhs, Derived *rhs)\n",
    "{\n",
    "    return lhs == rhs;\n",
    "}\n",
    "\n",
    "bool equal2(Base2* lhs, Derived *rhs)\n",
    "{\n",
    "    return lhs == rhs;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Вопрос на понимание:__ каков layout класса при ромбовидном наследовании? (лучше нарисовать)\n",
    "\n",
    "__Вопрос для продвинутых:__ как его оптимизировать?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Состояние класса, const + mutable"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Что считать состоянием класса?\n",
    "* значения всех его полей\n",
    "* логическое состояние класса (набор элементов в векторе, а не детали типа `capacity`)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "С точки зрения компилятора - набор всех его полей, не помеченных словом `mutable`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "private:\n",
    "    std::string name;\n",
    "    unsigned age;\n",
    "    \n",
    "public:\n",
    "    // аттрибут const означает, что метод не будет\n",
    "    // менять полей класса (и компилятор это проверит!)\n",
    "    //\n",
    "    // внутри const-методов поля имеют тип:\n",
    "    // const std::string name;\n",
    "    // const unsigned age;\n",
    "    bool is_too_young() const\n",
    "    {\n",
    "        return age <= 3;\n",
    "    }\n",
    "    \n",
    "    // метод не меняет полей класса, но программист\n",
    "    // забыл поставить const\n",
    "    void say_hello()\n",
    "    {\n",
    "        std::cout << \"hello\";\n",
    "    }\n",
    "    \n",
    "    // метод не меняет полей класса\n",
    "    void say_hello_twice() const\n",
    "    {\n",
    "        say_hello();  // COMPILE-TIME ERROR\n",
    "        say_hello();\n",
    "        \n",
    "        name = \"unk\";  // COMPILE-TIME ERROR\n",
    "    }\n",
    "    \n",
    "    // отсутствие const - метод МОЖЕТ менять поля\n",
    "    void happy_birthday()\n",
    "    {\n",
    "        age += 1; // OK\n",
    "    }   \n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Рассмотрим учебный пример - отображение, которое хранит ответ на последний запрос как кеширование."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Resources\n",
    "{\n",
    "public:\n",
    "    void add(int id, const std::string& resource);\n",
    "    \n",
    "    const std::string& get(int id) const;\n",
    "    \n",
    "private:\n",
    "    // хранилище ресурсов\n",
    "    std::map<int, std::string> id_to_resource;\n",
    "    \n",
    "    // закешированный последний запрошенный ресурс\n",
    "    mutable int last_id = -1;\n",
    "    mutable std::string last_resource;\n",
    "};\n",
    "\n",
    "void Resources::add(const int id, const std::string& resource)\n",
    "{\n",
    "    id_to_resource[id] = resource;\n",
    "}\n",
    "\n",
    "const std::string& Resources::get(const int id) const\n",
    "{\n",
    "    if (id == last_id)\n",
    "        return last_resource;\n",
    "    \n",
    "    auto it = id_to_resource.find(id);  // |find| - const-метод\n",
    "    \n",
    "    last_resource = it->second;  // OK, т.к. mutable\n",
    "    last_id = id;                // OK, т.к. mutable\n",
    "    \n",
    "    return last_resource;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Примечание: это ученический пример кеширования для демонстрации понятий `mutable` и состояния объекта, так кеширование реализовать не следует."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Операторы"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.cppreference.com/w/cpp/language/operators"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В виде свободных функций:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "struct Vec\n",
    "{\n",
    "    float x;\n",
    "    float y;\n",
    "};\n",
    "\n",
    "// v1 + v2\n",
    "Vec operator+ (const Vec& l, const Vec& r)\n",
    "{\n",
    "    return {l.x + r.x, l.y + r.y};\n",
    "}\n",
    "\n",
    "// v1 * v2\n",
    "float operator* (const Vec& l, const Vec& r)\n",
    "{\n",
    "    return l.x * r.x + l.y * r.y;\n",
    "}\n",
    "\n",
    "// v * a\n",
    "Vec operator* (const Vec& v, const float a)\n",
    "{\n",
    "    return {v.x * a, v.y * a};\n",
    "}\n",
    "\n",
    "// a * v\n",
    "Vec operator* (const float a, const Vec& v)\n",
    "{\n",
    "    return v * a;\n",
    "}\n",
    "\n",
    "// v / a\n",
    "Vec operator/ (const Vec& v, const float a)\n",
    "{\n",
    "    return v * (1. / a);\n",
    "}\n",
    "\n",
    "// v1 += v2;\n",
    "Vec& operator+= (Vec& l, const Vec& r)\n",
    "{\n",
    "    l.x += r.x;\n",
    "    l.y += r.y;\n",
    "    return l;\n",
    "}\n",
    "    \n",
    "// v1 -= v2;\n",
    "Vec& operator-= (Vec& l, const Vec& r)\n",
    "{\n",
    "    l.x -= r.x;\n",
    "    l.y -= r.y;\n",
    "    return l;\n",
    "}\n",
    "\n",
    "// v1 *= a\n",
    "Vec& operator*= (Vec& l, const float a)\n",
    "{\n",
    "    v1.x *= a;\n",
    "    v1.y *= a;\n",
    "    return v1;\n",
    "}\n",
    "\n",
    "// v1 /= a\n",
    "Vec& operator*= (Vec& l, const float a)\n",
    "{\n",
    "    l.x /= a;\n",
    "    l.y /= a;\n",
    "    return l;\n",
    "}\n",
    "\n",
    "// std::cout << v\n",
    "std::ostream& operator <<(std::ostream& os, const Vec& v)\n",
    "{\n",
    "    os << v.x << ',' << v.y;\n",
    "    return os;\n",
    "}\n",
    "\n",
    "bool operator< (const Vec& l, const Vec& r)\n",
    "{\n",
    "    return l.x != r.x ? l.x < r.x : l.y < r.y;\n",
    "}\n",
    "\n",
    "bool operator==(const Vec& l, const Vec& r)\n",
    "{\n",
    "    return l.x == r.x && l.y == r.y;\n",
    "}\n",
    "\n",
    "bool operator> (const Vec& l, const Vec& r) { return r < l; }\n",
    "bool operator<=(const Vec& l, const Vec& r) { return !(l > r); }\n",
    "bool operator>=(const Vec& l, const Vec& r) { return !(l < r); }\n",
    "bool operator!=(const Vec& l, const Vec& r) { return !(l == r); }\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В виде методов и функций - друзей:\n",
    "\n",
    "```c++\n",
    "// демонстрационный класс строки;\n",
    "// класс строки из стандартной библиотеки похитрее\n",
    "class String\n",
    "{\n",
    "private:\n",
    "    char* data;\n",
    "    size_t len;\n",
    "\n",
    "public:\n",
    "    String();\n",
    "    String(const char* s);\n",
    "    String(const String& rhs);\n",
    "    String(String&& rhs);\n",
    "    String& operator = (const String& rhs);\n",
    "    String& operator = (String&& rhs);\n",
    "    ~String();\n",
    "    \n",
    "    //\n",
    "    // operator[] может быть только в виде метода\n",
    "    //\n",
    "    // char c = s[i];\n",
    "    // s[i] = 'a';\n",
    "    //\n",
    "    // const String s = ....;\n",
    "    // s[2] = 'a';\n",
    "    char& operator[] (int i)\n",
    "    {\n",
    "        return data[i];\n",
    "    }\n",
    "    \n",
    "    const char& operator[] (std::size_t i) const\n",
    "    {\n",
    "        return data[i];\n",
    "    }\n",
    "    \n",
    "    // s += s1;\n",
    "    String& operator+= (const String& rhs)\n",
    "    {\n",
    "        // какая-то реализация ...\n",
    "        return *this;\n",
    "    }\n",
    "    \n",
    "    // s1 + s2\n",
    "    String operator+ (const String& rhs) const\n",
    "    {\n",
    "        String s = *this;\n",
    "        s += rhs;\n",
    "        return s;\n",
    "    }\n",
    "\n",
    "    // s1 *= n - повторить строку n раз\n",
    "    String& operator*= (std::size_t n)\n",
    "    {\n",
    "        // какая-то реализация ...\n",
    "        return *this;\n",
    "    }    \n",
    "    // Замечание: через операторы в виде методов сделать n * s1 нельзя\n",
    "    \n",
    "    // s1 * n\n",
    "    String operator* (std::size_t n) const\n",
    "    {\n",
    "        String s = *this;\n",
    "        s *= n;\n",
    "        return s;\n",
    "    }\n",
    "    \n",
    "    // std::cout << s\n",
    "    friend std::ostream& operator<<(std::ostream& os, const String& v)\n",
    "    {\n",
    "        return os << v.data;\n",
    "    }\n",
    "\n",
    "    friend bool operator< (const String& l, const String& r)\n",
    "    {\n",
    "        return /*...*/;\n",
    "    }\n",
    "\n",
    "    friend bool operator==(const String& l, const String& r)\n",
    "    {\n",
    "        return /*...*/;\n",
    "    }\n",
    "\n",
    "    friend bool operator> (const Vec& l, const Vec& r) { return r < l; }\n",
    "    friend bool operator<=(const Vec& l, const Vec& r) { return !(lhs > rhs); }\n",
    "    friend bool operator>=(const Vec& l, const Vec& r) { return !(lhs < rhs); }\n",
    "    friend bool operator!=(const Vec& l, const Vec& r) { return !(lhs == rhs); }\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Замечание**: разница между определением оператора внутри класса и вне класса:\n",
    "https://www.quora.com/What-is-the-difference-between-defining-an-overloaded-operator-outside-the-class-and-defining-it-inside-the-class/answer/Sergey-Zubkov-1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### static-поля класса"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Класс может иметь `static`-поля и `static`-методы:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`static`-поля можно рассматривать как глобальные константы / переменные:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "\n",
    "// Animal.h\n",
    "\n",
    "class Animal\n",
    "{\n",
    "public:\n",
    "    Animal(const std::string& a_name, unsigned a_age);\n",
    "    ...\n",
    "    \n",
    "    // declaration\n",
    "    static const std::string name_of_unknown;\n",
    "};\n",
    "\n",
    "// Animal.cpp\n",
    "\n",
    "// definition\n",
    "const std::string Animal::name_of_unknown = \"UNK\";\n",
    "\n",
    "// main.cpp\n",
    "\n",
    "// usage:\n",
    "void func()\n",
    "{\n",
    "    std::cout << \"name of uknown animal is: \" << Animal::name_of_unknown << std::endl;\n",
    "    \n",
    "    Animal animal(Animal::name_of_unknown, 12);\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "На `static`-поля распространяются модификаторы области доступа `private`/`protected`/`public`:\n",
    "* В случае `public` `static`-поле ведёт себя как обычная глобальная константа\n",
    "* В случае `private` только методы класса имеют доступ до этой константы"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Вопрос__: \n",
    "\n",
    "<details>\n",
    "<summary>Зачем могут быть полезны `static`-поля класса</summary>\n",
    "<p>\n",
    "   \n",
    "* Классоспецифичные константы.\n",
    "* Классоспецифичные глобальные переменные (по возможности избегать).\n",
    "   \n",
    "</p>\n",
    "</details>\n",
    "\n",
    "\n",
    "<details>\n",
    "<summary>Ещё примеры?</summary>\n",
    "<p>\n",
    "   \n",
    "* Класс DateTime. Можно в `public` вынести константы minute, second, hour ...\n",
    "* Ещё?\n",
    "   \n",
    "</p>\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### static-методы класса"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`static`-метод класса можно рассматривать как свободную friend-функцию:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "    int age = 0;\n",
    "\n",
    "public:\n",
    "    static void grow_up(int count, Animal& an)\n",
    "    {\n",
    "        an.age += count;\n",
    "    }\n",
    "\n",
    "    int get_age() const { return age; }\n",
    "};\n",
    "\n",
    "int main()\n",
    "{\n",
    "    Animal an;\n",
    "    Animal::grow_up(4, an);  // TODO: обратить внимание на вызов\n",
    "    std::cout << an.get_age() << std::endl;\n",
    "    return 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`static`-метод - обычная friend-функция, находящаяся в \"пространстве имён\" класса.\n",
    "\n",
    "**Для вызова static-метода _НЕ_ нужен объект класса**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Пример использования `static`-метода:\n",
    "\n",
    "```c++\n",
    "class House\n",
    "{\n",
    "    Floor floor;\n",
    "    Roof roof;\n",
    "    std::vector<Wall> walls;\n",
    "\n",
    "public:\n",
    "    House(Floor f, Roof r, std::vector<Wall> walls);\n",
    "    \n",
    "    static House make(const Config& config)\n",
    "    {\n",
    "        auto floor = Floor::make(config[\"floor\"]);\n",
    "        auto root = Roof::make(config[\"roof\"]);\n",
    "        auto walls = ...;        \n",
    "        return House(floor, roof, walls);\n",
    "    }    \n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Вопрос__: что-нибудь напоминает?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Вопрос__: когда оправдано использовать static-методы?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Виртуальные функции"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Какая функция будет вызвана. Нарисовать что будет происходить в памяти"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "struct A\n",
    "{\n",
    "\t~A() {}\n",
    "\tvoid f() {}\n",
    "\tvirtual void g() {}\n",
    "\tvoid h() {}\n",
    "};\n",
    "\n",
    "struct B : public A\n",
    "{\n",
    "\tvirtual ~B() {}\n",
    "\tvirtual void f() {}\n",
    "\tvoid g() {}\n",
    "\tvoid h() {}\n",
    "};\n",
    "\n",
    "struct C : public B\n",
    "{\n",
    "\t~C() {}\n",
    "\tvoid f() {}\n",
    "\tvoid g() {}\n",
    "\tvirtual void h() {}\n",
    "};\n",
    "\n",
    "int main()\n",
    "{\n",
    "\t{\n",
    "\t\tC c;\n",
    "\t\tc.f(); c.g(); C.h();\n",
    "\n",
    "\t\tB b = c;\n",
    "\t\tb.f(); b.g(); b.h();\n",
    "\n",
    "\t\tA& ar = c;\n",
    "\t\tar.f(); ar.g(); ar.h();\n",
    "\t}\n",
    "\n",
    "\t{\n",
    "\t\tC* cp = new C;\n",
    "\t\tA* ap = cp;\n",
    "\t\tap->f(); ap->g(); ap->h();\n",
    "\t\tdelete ap;\n",
    "\t}\n",
    "\n",
    "\t{\n",
    "\t\tC* cp = new C;\n",
    "\t\tB* bp = cp;\n",
    "        bp->f(); bp->g();\n",
    "\t\tdelete bp;\n",
    "\t}\n",
    "\n",
    "\treturn 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Какая функция будет вызвана?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class A {\n",
    "public:\n",
    "    A() {\n",
    "        init();\n",
    "    }\n",
    "\n",
    "    ~A() {\n",
    "        deinit();\n",
    "    }\n",
    "\n",
    "    virtual void init();\n",
    "    virtual void deinit();\n",
    "};\n",
    "\n",
    "class B : public A {\n",
    "public:\n",
    "    B() {}\n",
    "    ~B() {}\n",
    "\n",
    "    void init() override { ... }\n",
    "    void deinit() override { ... }\n",
    "};\n",
    "\n",
    "int main() {\n",
    "    B b;\n",
    "    return 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Виртуальные деструкторы: зачем и как"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Мы уже познакомились с механизмом виртуальных функций и поняли зачем и как они вызываются. Насчёт деструкторов рассмотрим такой пример:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "    int age;\n",
    "    \n",
    "public:\n",
    "    virtual void cry() = 0;\n",
    "};\n",
    "\n",
    "class Dog : public Animal\n",
    "{\n",
    "    std::string name;\n",
    "    \n",
    "public:\n",
    "    void cry() override;\n",
    "};\n",
    "\n",
    "class Human : public Animal\n",
    "{\n",
    "    std::string name;\n",
    "    std::string surname;\n",
    "    \n",
    "public:\n",
    "    void cry() override;\n",
    "};\n",
    "\n",
    "\n",
    "void func()\n",
    "{\n",
    "    Animal* animal = nullptr;\n",
    "    if (std::rand() % 2)\n",
    "        animal = new Dog;\n",
    "    else\n",
    "        animal = new Human;    \n",
    "    ...;    \n",
    "    delete animal;  // что здесь делать компилятору?\n",
    "}\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Поэтому реализуем иерархии так:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "    int age;\n",
    "\n",
    "public:\n",
    "    virtual ~Animal() = default;  // такой деструктор - ВИРТУАЛЬНЫЙ метод\n",
    "    virtual void cry() = 0;\n",
    "};\n",
    "\n",
    "// далее как обычно\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### const-методы"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Упражнение: какая функция будет вызвана"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class A\n",
    "{\n",
    "public:\n",
    "\tvoid f() { std::cout << \"f();\"; }\n",
    "\tvoid f() const { std::cout << \"f() const;\" }\n",
    "};\n",
    "\n",
    "\n",
    "int main()\n",
    "{\n",
    "\tA a;\n",
    "\ta.f();\n",
    "\n",
    "\tconst A ca;\n",
    "\tca.f();\n",
    "\n",
    "\tconst A& car = a;\n",
    "\tcar.f();\n",
    "\n",
    "\tA& ar = a;\n",
    "\tar.f();\n",
    "\n",
    "\tA* const acp = &a;\n",
    "\tacp->f();\n",
    "\n",
    "\tconst A* cap = &a;\n",
    "\tcap->f();\n",
    "\n",
    "\tconst A a2 = a;\n",
    "\ta2.f();\n",
    "\n",
    "\treturn 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### class vs struct"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* `class` - если есть инвариант\n",
    "* `struct` - если поля независимы"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "struct Point\n",
    "{\n",
    "\tdouble x;\n",
    "\tdouble y;\n",
    "};\n",
    "\n",
    "struct Color\n",
    "{\n",
    "\tchar r;\n",
    "\tchar g;\n",
    "\tchar b;\n",
    "};\n",
    "\n",
    "// ???\n",
    "struct Size\n",
    "{\n",
    "\tdouble w;\n",
    "\tdouble h;\n",
    "};\n",
    "\n",
    "struct Rectangle\n",
    "{\n",
    "\tPoint origin;\n",
    "\tSize size;\n",
    "};\n",
    "\n",
    "class JuiceBottle\n",
    "{\n",
    "private:\n",
    "\tdouble max_volume_;\n",
    "\tdouble cur_volume_;\n",
    "};\n",
    "\n",
    "class String\n",
    "{\n",
    "private:\n",
    "\tchar *s_;\n",
    "\tsize_t len_;\n",
    "\tsize_t capacity_;\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Принципы дизайна класса"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* RAII\n",
    "* Есть инвариант - class, поля независимы - struct\n",
    "* Если есть подклассы - делаем `virtual` деструктор\n",
    "* Правило 6\n",
    "    * `=default` где достаточно автогенерённого\n",
    "    * `=delete` где нужно запретить\n",
    "    \n",
    "    ```c++\n",
    "    class Animal\n",
    "    {\n",
    "    private:\n",
    "        std::string name;\n",
    "\n",
    "    public:\n",
    "        Animal() : name(\"unknown\") {}\n",
    "        Animal(const Animal&) = default;\n",
    "        Animal(Animal&&) noexcept = delete;\n",
    "        Animal& operator= (const Animal&) = default;\n",
    "        Animal& operator= (Animal&&) = delete;\n",
    "        ~Animal() noexcept = default;\n",
    "    };\n",
    "    ```\n",
    "\n",
    "* `noexcept` d-tor, move operators (подробнее на лекции про исключения)\n",
    "* `const` на все методы, которые не меняют состояния класса (значения полей)\n",
    "\n",
    "    ```c++\n",
    "    class Animal\n",
    "    {\n",
    "    private:\n",
    "        std::string name;\n",
    "\n",
    "    public:\n",
    "        const std::string& get_name() const noexcept { return name; }\n",
    "    };\n",
    "    ```\n",
    "\n",
    "* `override` на переопределённые `virtual` методы\n",
    "\n",
    "    ```c++\n",
    "    class Animal\n",
    "    {\n",
    "    public:\n",
    "        virtual void say() = 0;\n",
    "    };\n",
    "\n",
    "    class Cat : public Animal\n",
    "    {\n",
    "    public:\n",
    "        void say() override;\n",
    "    };\n",
    "    ```\n",
    "\n",
    "* Какой функционал делать методом, а какой - свободной фунцкией? Всопним ADL. Благодаря ему есть философский принцип, что функции, принимающие первым аргументом объект класса (или ссылку), являются частью интерфейса класса:\n",
    "\n",
    "    ```c++\n",
    "    struct Vector3D\n",
    "    {\n",
    "        double x, y, z;\n",
    "    };\n",
    "\n",
    "    // философски часть интерфейса класса Vector3D\n",
    "    double length(const Vector3D v);\n",
    "    ```\n",
    "\n",
    "    Поэтому принцип рационального минмимума в методах:\n",
    "      * struct Vector3D - всё в свободные функции (normalize, length, rotate ...))\n",
    "      * vector<int> (pointer, size, capacity: size() - member; at() - member; sort() - function; empty() - ???; middle element - function; max() - function, mean() - function)\n",
    "      * cache (add, get, del; clear? - в метод, т.к. эффективнее реализовать, обладая доступом до полей)\n",
    "\n",
    "    Нужна __причина__, чтобы что-нибудь сделать методом:\n",
    "        * требуется доступ до приватных полей и нельзя реализовать иначе (vector.size(), vector.at(), vector.push_back())\n",
    "        * реализация через приватные поля проще и быстрее (cache.clear())\n",
    "        * лучше вписывается в сщуествующие методы класса (vector.empty())\n",
    "        * что-нибудь ещё?\n",
    "\n",
    "* Никогда не используйте `protected`-поля\n",
    "\n",
    "* Иногда нужно менять поля в `const`-методах, и это не влияет на состояние класса в умозрительном смысле. Используйте `mutable`:\n",
    "    * Кеширование: бинарное дерево, которое запоминает последний запрос\n",
    "    * `mutex`\n",
    "    * отложенная инициализация\n",
    "\n",
    "* SRP - Single Responsibility Principle - Принцип единственной ответственности - класс должен быть экспертом в одной и только одной области. Такие классы проще отлаживать и тестировать. Всегда понятно, ошибка в рамках его области ответственности или нет."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Back to Basics: Designing Classes (part 1 of 2) - Klaus Iglberger - CppCon 2021](https://youtu.be/motLOioLJfg)  \n",
    "[Back to Basics: Designing Classes (part 2 of 2) - Klaus Iglberger - CppCon 2021](https://youtu.be/O65lEiYkkbc)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
